package com.bluetrum.abmate.ota;

import android.annotation.TargetApi;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

import com.bluetrum.abmate.R;
import com.bluetrum.abmate.databinding.ActivityBluetoothOtaBinding;
import com.bluetrum.abmate.models.ABEarbuds;
import com.bluetrum.abmate.utils.Utils;
import com.bluetrum.abmate.viewmodels.SharedViewModel;
import com.bluetrum.devicemanager.DeviceCommManager;
import com.bluetrum.devicemanager.cmd.Request;
import com.bluetrum.devicemanager.models.ABDevice;
import com.bluetrum.fota.OtaError;
import com.bluetrum.fota.OtaManager;
import com.bluetrum.fota.cmd.notification.OtaStateNotificationCallable;
import com.bluetrum.fota.cmd.request.OtaRequest;
import com.bluetrum.fota.cmd.response.OtaInfoResponseCallable;
import com.bluetrum.utils.FileUtils;
import com.bluetrum.utils.HexUtils;
import com.example.simplefileexplorer.SimpleFileExplorerActivity;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import dagger.hilt.android.AndroidEntryPoint;
import timber.log.Timber;

@AndroidEntryPoint
final public class OtaActivity extends AppCompatActivity
        implements OtaManager.RequestDelegate, OtaManager.EventListener {

    public static final int OTA_FILE_CHECKSUM_POSITION = 56;

    private static final int file_request_result_code = 666;

    private SharedViewModel mViewModel;

    private ActivityBluetoothOtaBinding binding;

    private TextView filePathTextView;
    private TextView statusTextView;
    private Button updateButton;

    private ViewGroup progressView;
    private ProgressBar progressBar;
    private TextView progressTextView;

    private DeviceCommManager commManager;
    private OtaManager otaManager;

    private byte[] fileChecksum = null;
    private byte[] fwChecksum = null;

    //////////////////
    // TODO: ！！！测试用，不要在正式版软件中使用！！！
    // TODO: !!! For testing, DO NOT use in release version !!!
    @TargetApi(Build.VERSION_CODES.R)
    private void gotoManageExternalStorage() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }
    //////////////////

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewModel = new ViewModelProvider(this).get(SharedViewModel.class);
        binding = ActivityBluetoothOtaBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        filePathTextView = binding.filePathTextView;;
        statusTextView = binding.statusTextView;
        updateButton = binding.updateButton;
        progressView = binding.progressView;
        progressBar = binding.progressBar;
        progressTextView = binding.progressTextView;

        //////////////////
        binding.permissionButton.setOnClickListener(view -> gotoManageExternalStorage());
        /////////////////

        updateButton.setOnClickListener(this::clickUpdateButton);

        mViewModel.getActiveDevice().observe(this, this::onActiveDeviceChanged);
        mViewModel.getDeviceFirmwareVersion().observe(this, this::showFirmwareVersion);
        mViewModel.getDeviceFWChecksum().observe(this, this::showFirmwareChecksum);
        mViewModel.getDeviceIsTws().observe(this, this::onSupportTws);
        mViewModel.getDeviceTwsConnected().observe(this, this::onTwsConnected);

        otaManager = new OtaManager(this, this);

        commManager = mViewModel.getDeviceRepository().getDeviceCommManager();
        commManager.registerNotificationCallback(OtaRequest.COMMAND_OTA_STATE, OtaStateNotificationCallable.class, otaManager);
        commManager.registerResponseCallable(OtaRequest.COMMAND_OTA_GET_INFO, OtaInfoResponseCallable.class);

        otaManager.setRetryCallback(new OtaManager.RetryCallback() {
            @Override
            public void beforeRetryCallback() {
                // Increasing send interval if using SPP
                // It's not necessary in general. Uncomment it if necessary
                ABDevice device = mViewModel.getActiveDevice().getValue();
                if (device instanceof ABEarbuds) {
                    ABEarbuds earbuds = (ABEarbuds) device;
                    earbuds.setSppSendInterval(earbuds.getSppSendInterval() + 1);
                }
            }
        });

        statusTextView.setText(R.string.ota_select_file);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Timber.d("onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Timber.d("onResume");

        //////////////////
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
            binding.permissionButton.setVisibility(View.VISIBLE);
        } else {
            binding.permissionButton.setVisibility(View.INVISIBLE);
        }
        /////////////////
    }

    @Override
    protected void onPause() {
        super.onPause();
        Timber.d("onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Timber.d("onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        commManager.unregisterResponseCallable(OtaRequest.COMMAND_OTA_GET_INFO);
        commManager.unregisterNotificationCallback(OtaRequest.COMMAND_OTA_STATE);
        if (otaManager != null) {
            otaManager.reset();
            otaManager = null;
        }
    }

    @Override
    public void onBackPressed() {
        if (otaManager.isUpdating()) {
            Toast.makeText(this, R.string.ota_toast_updating, Toast.LENGTH_SHORT).show();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_ota, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem item = menu.findItem(R.id.select);
        if (item != null) {
            item.setEnabled(checkIfDeviceReady() && !otaManager.isUpdating());
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        final int itemId = item.getItemId();
        if (R.id.select == itemId) {
            // 打开文件选择器
            String position = FileUtils.getLastPosition(this);
            File dir = new File(position);
            if (!dir.exists()) {
                position = Environment.getExternalStorageDirectory().getAbsolutePath();
            }
            startFileExplorerLibrary(position);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void startFileExplorerLibrary(String dirPath){
        Intent intent = new Intent(this, SimpleFileExplorerActivity.class);
        intent.putExtra(SimpleFileExplorerActivity.ENABLE_DIRECTORY_SELECT_KEY, false);
        intent.putExtra(SimpleFileExplorerActivity.ON_ACTIVITY_RESULT_KEY, dirPath);
        startActivityForResult(intent, file_request_result_code);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 处理文件选择返回
        if (resultCode == RESULT_OK) {
            if (requestCode == file_request_result_code) {
                if (data != null) {
                    String selectedAbsolutePath =
                            data.getStringExtra(SimpleFileExplorerActivity.ON_ACTIVITY_RESULT_KEY);
                    if (selectedAbsolutePath != null) {
                        FileUtils.saveLastPosition(this, FileUtils.getDirFromFilePath(selectedAbsolutePath));
                        filePathTextView.setText(selectedAbsolutePath);
                        // 读取OTA文件
                        readOtaFile(selectedAbsolutePath);
                    }
                }
            }
        }
    }

    private void showFirmwareVersion(Integer version) {
        if (version != null) {
            int v1 = (version >> 24) & 0xFF;
            int v2 = (version >> 16) & 0xFF;
            int v3 = (version >> 8) & 0xFF;
            int v4 = version & 0xFF;
            String versionString = getString(R.string.ota_current_firmware_version, v1, v2, v3, v4);
            Timber.d("Current FW Ver：%s", versionString);
            binding.versionTextView.setText(versionString);
        }
    }

    private void showFirmwareChecksum(byte[] checksum) {
        if (checksum != null && checksum.length == 4) {
            // 显示校验
            fwChecksum = checksum;
            String fwcs = HexUtils.bytesToHex(checksum);
            Timber.d("Current FW Checksum：%s", fwcs);
            binding.fwChecksumTextView.setText(fwcs);
            binding.fwChecksumLayout.setVisibility(View.VISIBLE);
        }
        checkFWChecksum();
    }

    // 蓝讯原厂FOT文件不包含固件信息，可自行进行封装，读取时自行解析，并设置固件版本用于版本判断
    private void readOtaFile(String filePath) {
        // 读取文件在库外部进行处理
        byte[] otaData;
        try {
            otaData = FileUtils.readFile(filePath);
            otaManager.setOtaData(otaData);
        } catch (Exception e) {
            String errorDesc = getString(R.string.ota_read_file_error);
            Timber.e(e);
            statusTextView.setText(errorDesc);
            return;
        }

        fileChecksum = null;
        // 大小不会小于固件的校验位置+长度
        if (otaData.length >= OTA_FILE_CHECKSUM_POSITION + 4) {
            // 显示校验
            byte[] checksum = new byte[4];
            checksum[0] = otaData[OTA_FILE_CHECKSUM_POSITION + 3];
            checksum[1] = otaData[OTA_FILE_CHECKSUM_POSITION + 2];
            checksum[2] = otaData[OTA_FILE_CHECKSUM_POSITION + 1];
            checksum[3] = otaData[OTA_FILE_CHECKSUM_POSITION];
            fileChecksum = checksum;

            String fcs = HexUtils.bytesToHex(checksum);
            binding.fileChecksumTextView.setText(fcs);
            Timber.d("Current File Checksum：%s", fcs);
            binding.fileChecksumLayout.setVisibility(View.VISIBLE);
        }
        checkFWChecksum();

        // 自定义版本号，或者封装FOT文件，从中取版本号
        otaManager.setFirmwareVersion(0xFFFFFFFF);

        // 开始准备升级，获取信息
        otaManager.prepareToUpdate();
    }

    private boolean checkFWChecksum() {
        if (fwChecksum != null && fileChecksum != null) {
            boolean b = Arrays.equals(fwChecksum, fileChecksum);
            binding.noNeedToUpdateTextView.setVisibility(b ? View.VISIBLE : View.GONE);
            return b;
        }
        return false;
    }

    private boolean checkIfDeviceReady() {
        ABDevice device = mViewModel.getActiveDevice().getValue();
        Boolean isTws = mViewModel.getDeviceIsTws().getValue();
        Boolean connected = mViewModel.getDeviceTwsConnected().getValue();

        if (device != null) {
            if (isTws != null && isTws) {
                return connected != null && connected;
            }
            return true;
        }
        return false;
    }

    private void onActiveDeviceChanged(final ABDevice activeDevice) {
        if (activeDevice != null) {
            statusTextView.setText(R.string.ota_device_connected);
            updateButton.setEnabled(true);
        } else {
            statusTextView.setText(R.string.ota_device_disconnected);
            updateButton.setEnabled(false);
            updateButton.setVisibility(View.INVISIBLE);
            setKeepScreenOn(false);
            updateButton.setBackgroundColor(Color.BLUE);
            otaManager.reset();
        }
        invalidateOptionsMenu();
    }

    private void onSupportTws(final Boolean isTws) {
        handleTWSMessage();
    }

    private void onTwsConnected(final Boolean connected) {
        handleTWSMessage();
    }

    private void handleTWSMessage() {
        Boolean isTws = mViewModel.getDeviceIsTws().getValue();
        Boolean connected = mViewModel.getDeviceTwsConnected().getValue();

        if (isTws != null) {
            if (isTws) {
                binding.twsTextView.setVisibility(View.VISIBLE);
                binding.twsConnectedTextView.setVisibility(View.VISIBLE);

                if (connected != null && connected) {
                    binding.twsConnectedTextView.setText(R.string.ota_tws_connected);
                } else {
                    binding.twsConnectedTextView.setText(R.string.ota_tws_disconnected);
                }
            }
        }
        // 更新选项菜单
        invalidateOptionsMenu();

        if (otaManager.isUpdating()) {
            if (!checkIfDeviceReady()) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle(R.string.ota_alert_quit)
                        .setPositiveButton(R.string.ok, (dialog, which) -> OtaActivity.this.finish())
                        .show();
            }
        } else {
            if (checkIfDeviceReady() && otaManager.isReadyToUpdate()) {
                updateButton.setEnabled(true);
                updateButton.setVisibility(View.VISIBLE);
                updateButton.setBackgroundResource(android.R.drawable.btn_default);
            } else {
                updateButton.setEnabled(false);
                updateButton.setVisibility(View.INVISIBLE);
                statusTextView.setText("");
            }
        }
    }

    private void clickUpdateButton(View view) {
        if (otaManager.isReadyToUpdate()) {
            otaManager.startOTA();
        }
    }

    private void showProgress(int progress) {
        progressBar.setProgress(progress);
        progressTextView.setText(String.format(Locale.getDefault(), "%d%%", progress));
    }

    private void setKeepScreenOn(boolean keepScreenOn) {
        progressBar.setKeepScreenOn(keepScreenOn);
    }

    private void showErrorDescription(int errorCode) {
        String errorDescription;
        switch (errorCode) {
            case OtaError.ERROR_CODE_SAME_FIRMWARE:
                errorDescription = getString(R.string.ota_error_same_firmware);
                break;
            case OtaError.ERROR_CODE_KEY_MISMATCH:
                errorDescription = getString(R.string.ota_error_key_mismatch);
                break;
            case OtaError.ERROR_CODE_CRC_ERROR:
                errorDescription = getString(R.string.ota_error_crc_error);
                break;
            case OtaError.ERROR_CODE_WRONG_SEQ_NUM:
                errorDescription = getString(R.string.ota_error_wrong_seq_num);
                break;
            case OtaError.ERROR_CODE_WRONG_DATA_LEN:
                errorDescription = getString(R.string.ota_error_wrong_data_len);
                break;
            case OtaError.ERROR_CODE_TWS_DISCONNECTED:
                errorDescription = getString(R.string.ota_error_tws_disconnected);
                break;
            default:
                String errorString = getString(R.string.ota_error_unknown);
                errorDescription = errorString + errorCode;
        }
        String errorString = getString(R.string.ota_error_alert, errorDescription);
        statusTextView.setText(errorString);
    }

    /* OtaRequestDelegate */

    @Override
    public void sendRequest(@NonNull Request request) {
        commManager.sendRequest(request);
    }

    @Override
    public void sendRequest(@NonNull Request request,
                            @NonNull DeviceCommManager.RequestCallback requestCallback) {
        commManager.sendRequest(request, requestCallback);
    }

    /* OTA EventListener */

    @Override
    public void onOtaAllowUpdate(boolean allowed) {
        if (allowed) {
            otaManager.setPacketSize(commManager.getMaxPayloadSize());

            statusTextView.setText(R.string.ota_ready_to_update);
            updateButton.setEnabled(true);
            updateButton.setVisibility(View.VISIBLE);
            invalidateOptionsMenu();
        } else {
            Utils.showToast(OtaActivity.this, R.string.ota_device_refused_the_update);
            statusTextView.setText(R.string.ota_device_refused_the_update);
        }
    }

    @Override
    public void onOtaStart() {
        statusTextView.setText(R.string.ota_updating);
        progressView.setVisibility(View.VISIBLE);
        setKeepScreenOn(true);
        updateButton.setEnabled(false);
        invalidateOptionsMenu();
        updateButton.setBackgroundColor(Color.YELLOW);
    }

    @Override
    public void onOtaProgress(int progress) {
        showProgress(progress);
    }

    @Override
    public void onOtaFinish() {
        progressTextView.setText(R.string.ota_update_finished);
        statusTextView.setText(R.string.ota_update_finished);
        setKeepScreenOn(false);
        updateButton.setBackgroundColor(Color.GREEN);
    }

    @Override
    public void onOtaPause() {
        statusTextView.setText(R.string.ota_update_pause);
    }

    @Override
    public void onOtaContinue() {
        statusTextView.setText(R.string.ota_updating);
    }

    @Override
    public void onOtaError(int errorCode) {
        showErrorDescription(errorCode);
        setKeepScreenOn(false);
        updateButton.setBackgroundColor(Color.RED);
        invalidateOptionsMenu();
    }
}
